/*
 * Copyright 2011 LMAX Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lmax.disruptor;

/**
 * [ANALYSIS] 阻塞等待策略 - 基于传统锁机制的低CPU消耗等待策略
 * 
 * [DESIGN] 设计理念：
 * 使用Java传统的synchronized锁和Object.wait()/notifyAll()机制实现消费者等待
 * 当没有新事件时，消费者线程会被阻塞，释放CPU资源给其他线程使用
 * 生产者发布事件后，通过notifyAll()唤醒所有等待的消费者线程
 * 
 * [ALGORITHM] 等待算法：
 * 1. 首次检查：如果cursor序列号小于期望序列号，进入阻塞等待
 * 2. 同步阻塞：在mutex对象上synchronized，然后调用wait()阻塞
 * 3. 循环检查：被唤醒后重新检查序列号，如果仍不满足则继续wait()
 * 4. 依赖等待：即使cursor满足要求，还需等待dependentSequence也满足
 * 5. 最终确认：使用Thread.onSpinWait()进行少量自旋等待
 * 
 * [PERFORMANCE] 性能特征：
 * - CPU使用率：极低，线程阻塞时不消耗CPU资源
 * - 延迟：相对较高，唤醒和上下文切换有开销
 * - 吞吐量：中等，适合事件频率不高的场景
 * - 系统调用：频繁使用synchronized和wait/notify系统调用
 * 
 * [CONCURRENCY] 并发特性：
 * - 线程安全：通过synchronized保证多线程安全
 * - 唤醒机制：使用notifyAll()确保所有等待线程都被唤醒
 * - 阻塞队列：多个消费者线程可以同时在mutex上等待
 * - 上下文切换：线程阻塞和唤醒会发生上下文切换
 * 
 * [USAGE] 适用场景：
 * ✅ 事件频率较低的系统（< 1000 events/sec）
 * ✅ CPU资源受限的环境
 * ✅ 多消费者且消费速度不匹配的场景
 * ✅ 对延迟要求不严格的后台处理系统
 * ❌ 高频交易等延迟敏感场景
 * ❌ 需要微秒级响应时间的实时系统
 * 
 * [COMPARISON] 与其他策略对比：
 * vs BusySpinWaitStrategy: CPU消耗低100倍，但延迟高10-100倍
 * vs YieldingWaitStrategy: 延迟稍高，但CPU友好度明显更好
 * vs SleepingWaitStrategy: 更彻底的阻塞，适合更低频的场景
 * 
 * 使用示例：
 * <pre>{@code
 * // 创建使用阻塞等待策略的RingBuffer
 * RingBuffer<Event> ringBuffer = RingBuffer.createMultiProducer(
 *     Event::new, 1024, new BlockingWaitStrategy());
 * 
 * // 适合CPU资源有限或事件频率不高的场景
 * }</pre>
 * 
 * @see WaitStrategy 等待策略接口
 * @see YieldingWaitStrategy 让出CPU的等待策略
 * @see SleepingWaitStrategy 睡眠等待策略
 */
public final class BlockingWaitStrategy implements WaitStrategy
{
    // [CONCURRENCY] 同步互斥对象 - 实现线程间的等待和唤醒机制
    // 作为wait()/notify()的监视器对象，确保消费者线程的阻塞和唤醒操作线程安全
    // 使用final确保引用不可变，避免意外的监视器对象更换
    private final Object mutex = new Object();

    /**
     * [WORKFLOW] 阻塞等待指定序列号变为可用 - 两阶段等待机制的核心实现
     * 
     * [ALGORITHM] 等待算法实现：
     * 阶段1：RingBuffer游标等待 - 确保生产者已发布目标序列号
     * 1. 检查cursorSequence是否已达到目标序列号
     * 2. 如果未达到，在mutex上进行synchronized阻塞
     * 3. 循环调用wait()直到生产者发布并唤醒
     * 
     * 阶段2：依赖序列号等待 - 确保前置消费者已处理完成
     * 1. 检查dependentSequence是否满足处理顺序要求
     * 2. 使用轻量级自旋等待，避免过度的阻塞开销
     * 3. 定期检查alert状态，支持优雅停机
     * 
     * [PERFORMANCE] 性能优化考虑：
     * - 分阶段等待：先等待生产者发布，再等待依赖满足，避免不必要的阻塞
     * - 混合策略：主要使用阻塞等待，依赖等待使用轻量自旋
     * - 减少锁竞争：只在必要时才进入synchronized块
     * - 早期检查：优先进行无锁的序列号检查
     * 
     * [CONCURRENCY] 并发协调机制：
     * - 生产者协调：通过cursorSequence和signalAllWhenBlocking()协调
     * - 消费者协调：通过dependentSequence确保处理顺序
     * - 异常处理：定期检查barrier的alert状态，响应停机信号
     * - 中断处理：wait()方法会响应线程中断，支持优雅停止
     * 
     * [BLOCKING] 阻塞机制详解：
     * - synchronized(mutex)：获取监视器锁，确保wait/notify的原子性
     * - mutex.wait()：释放锁并阻塞当前线程，等待notify唤醒
     * - 循环检查：被唤醒后重新检查条件，处理虚假唤醒情况
     * - 自动释放：wait()期间自动释放mutex锁，其他线程可以进入
     * 
     * [MEMORY] 内存可见性：
     * - cursorSequence.get()：volatile读，确保看到生产者的最新发布
     * - dependentSequence.get()：volatile读，确保看到前置消费者的进度
     * - synchronized块：提供内存屏障，确保缓存一致性
     * 
     * @param sequence 期望等待的序列号
     * @param cursorSequence RingBuffer的游标序列号，由生产者更新
     * @param dependentSequence 依赖的序列号，确保消费顺序
     * @param barrier 序列号屏障，提供alert检查功能
     * @return 实际可用的序列号，通常等于或大于请求的序列号
     * @throws AlertException 当Disruptor状态改变时抛出
     * @throws InterruptedException 当线程被中断时抛出
     */
    @Override
    public long waitFor(final long sequence, final Sequence cursorSequence, final Sequence dependentSequence, final SequenceBarrier barrier)
        throws AlertException, InterruptedException
    {
        long availableSequence;
        
        // [PHASE-1] 第一阶段：等待生产者发布目标序列号
        if (cursorSequence.get() < sequence)
        {
            // [BLOCKING] 进入synchronized阻塞等待阶段
            synchronized (mutex)
            {
                // [LOOP] 循环等待，处理虚假唤醒和并发更新
                while (cursorSequence.get() < sequence)
                {
                    // [ALERT] 检查停机信号，支持优雅关闭
                    barrier.checkAlert();
                    // [WAIT] 释放锁并阻塞线程，等待生产者唤醒
                    mutex.wait();
                }
            }
        }

        // [PHASE-2] 第二阶段：等待依赖序列号满足（使用轻量级自旋）
        while ((availableSequence = dependentSequence.get()) < sequence)
        {
            // [ALERT] 再次检查停机信号
            barrier.checkAlert();
            // [SPIN] 轻量级自旋等待，提示JVM这是自旋循环
            Thread.onSpinWait();
        }

        // [RETURN] 返回实际可用的最大序列号（支持批量处理）
        return availableSequence;
    }

    /**
     * [WORKFLOW] 唤醒所有阻塞等待的消费者线程 - 生产者发布事件后的通知机制
     * 
     * [ALGORITHM] 唤醒算法：
     * 1. 获取mutex监视器锁（synchronized）
     * 2. 调用notifyAll()唤醒所有在mutex上等待的线程
     * 3. 释放监视器锁，被唤醒的线程开始竞争重新获取锁
     * 
     * [CONCURRENCY] 并发唤醒机制：
     * - 线程安全：通过synchronized确保唤醒操作的原子性
     * - 全量唤醒：使用notifyAll()而非notify()，确保所有等待线程都被唤醒
     * - 竞争唤醒：被唤醒的线程需要重新竞争mutex锁才能继续执行
     * - 条件重检：唤醒后线程会重新检查序列号条件，避免虚假唤醒问题
     * 
     * [PERFORMANCE] 性能考虑：
     * - 系统调用开销：synchronized和notifyAll()都是重量级系统调用
     * - 上下文切换：唤醒操作会触发多次线程上下文切换
     * - 锁竞争：多个被唤醒的线程会竞争mutex锁，可能导致锁竞争
     * - 调用频率：每次事件发布都会调用，高频场景下开销明显
     * 
     * [COORDINATION] 与其他组件的协调：
     * - 调用时机：由Sequencer.publish()方法调用
     * - 等待线程：对应waitFor()方法中wait()的线程
     * - 生产者：通常在发布序列号后立即调用
     * - 多消费者：支持同时唤醒多个消费者线程
     * 
     * [DESIGN] 设计权衡：
     * ✅ 确保所有等待线程都能及时收到通知
     * ✅ 避免线程饥饿和死锁问题
     * ✅ 支持多消费者场景下的公平唤醒
     * ❌ 系统调用开销相对较高
     * ❌ 可能产生不必要的唤醒（如果多个消费者等待不同序列号）
     * 
     * @see #waitFor(long, Sequence, Sequence, SequenceBarrier) 对应的等待方法
     * @see Sequencer#publish(long) 调用此方法的发布逻辑
     */
    @Override
    public void signalAllWhenBlocking()
    {
        // [SYNCHRONIZATION] 获取监视器锁，确保唤醒操作的原子性
        synchronized (mutex)
        {
            // [NOTIFY] 唤醒所有在此mutex上等待的消费者线程
            // 使用notifyAll()而非notify()确保所有等待线程都被唤醒
            mutex.notifyAll();
        }
        // [RELEASE] synchronized块结束，自动释放监视器锁
    }

    @Override
    public String toString()
    {
        return "BlockingWaitStrategy{" +
            "mutex=" + mutex +
            '}';
    }
}
